<?php

namespace JsonLowCode\Factory;

use BadMethodCallException;
use Closure;
use JsonLowCode\Traits\Macroable;
use JsonSerializable;
use JsonLowCode\Contracts\FactoryInstance;
use JsonLowCode\UI;
use JsonLowCode\UI\AppForm;
use JsonLowCode\UI\Divider;
use JsonLowCode\UI\FormField;
use JsonLowCode\Form;
use JsonLowCode\Form\Fields;
use JsonLowCode\Traits\HasMake;

/**
 * FormFactory 工厂类
 * @method Fields\HiddenField hidden(string $field, mixed $label = null) 普通输入框
 * @method Fields\TextField text(string $field, mixed $label = null) 普通输入框
 * @method Fields\NumberField number(string $field, mixed $label = null) 数字输入框
 * @method Fields\CurrencyField currency(string $field, mixed $label = null) 货币输入框
 * @method Fields\PasswordField password(string $field, mixed $label = null) 密码输入框
 * @method Fields\TextAreaField textarea(string $field, mixed $label = null) 文本输入框
 * @method Fields\RadioField radio(string $field, mixed $label = null) radio按钮
 * @method Fields\CheckBoxField checkbox(string $field, mixed $label = null) radio按钮
 * @method Fields\RateField rate(string $field, mixed $label = null) 评分组件
 * @method Fields\SwitchField switch(string $field, mixed $label = null) switch开关
 * @method Fields\SliderField slider(string $field, mixed $label = null) 滑块组件
 * @method Fields\ColorPickerField color(string $field, mixed $label = null) 颜色组件
 * @method Fields\IconSelectField icon(string $field, mixed $label = null) 图标选择组件
 * @method Fields\DatePickerField date(string $field, mixed $label = null) 日期组件
 * @method Fields\DateTimePickerField datetime(string $field, mixed $label = null) 日期时间组件
 * @method Fields\DateYearPickerField year(string $field, mixed $label = null) 年选择组件
 * @method Fields\DateMonthPickerField month(string $field, mixed $label = null) 月选择组件
 * @method Fields\DateQuarterPickerField quarter(string $field, mixed $label = null) 季选择组件
 * @method Fields\TimePickerField time(string $field, mixed $label = null) 时间选择组件
 * @method Fields\DateRangePickerField dateRange(string $field, mixed $label = null) 日期范围组件
 * @method Fields\DynamicInputField dynamicInput(string $field, mixed $label = null) 动态Input组件
 * @method Fields\DynamicTagsField dynamicTags(string $field, mixed $label = null) 动态标签组件
 * @method Fields\ObjectListField objectList(string $field, mixed $label = null) objectList对象数组组件
 * @method Fields\ListField list(string $field, mixed $label = null) 一维数组组件
 * @method Fields\SfcLocalField sfcLocal(string $field, mixed $label = null) sfcLocal 本地sfc组件
 * @method Fields\SfcApiField sfcApi(string $field, mixed $label = null) sfcApi 远程sfc组件
 * @method Fields\ImageVerifyField imageVerify(string $field, mixed $label = null) ImageVerifyField 图片验证码
 */
abstract class FormFactory implements FactoryInstance, JsonSerializable
{

    use Macroable, HasMake;

    protected AppForm $form;

    protected AppForm $element;

    protected string $bindKey = "";

    /**
     * @var bool 是否不使用 FormField
     */
    protected bool $noItem = false;

    /**
     * 模式: form模式, make模式
     * @var string
     */
    protected string $mode = "form";

    protected array $elements = [];

    /**
     * @var array<string,mixed>
     */
    protected array $rules = [];

    protected array $methods = [
        'hidden'    =>  Form\Fields\HiddenField::class,
        'text'      =>  Form\Fields\TextField::class,
        'number'    =>  Form\Fields\NumberField::class,
        'currency'  =>  Form\Fields\CurrencyField::class,
        'password'  =>  Form\Fields\PasswordField::class,
        'textarea'  =>  Form\Fields\TextAreaField::class,
        'radio'     =>  Form\Fields\RadioField::class,
        'checkbox'  =>  Form\Fields\CheckBoxField::class,
        'rate'      =>  Form\Fields\RateField::class,
        'switch'    =>  Form\Fields\SwitchField::class,
        'slider'    =>  Form\Fields\SliderField::class,
        'color'     =>  Form\Fields\ColorPickerField::class,
        'icon'      =>  Form\Fields\IconSelectField::class,
        'year'      =>  Form\Fields\DateYearPickerField::class,
        'month'     =>  Form\Fields\DateMonthPickerField::class,
        'quarter'   =>  Form\Fields\DateQuarterPickerField::class,
        'date'      =>  Form\Fields\DatePickerField::class,
        'time'      =>  Form\Fields\TimePickerField::class,
        'datetime'  =>  Form\Fields\DateTimePickerField::class,
        'dateRange'   =>  Form\Fields\DateRangePickerField::class,
        'dynamicInput'    =>  Form\Fields\DynamicInputField::class,
        'dynamicTags'     =>  Form\Fields\DynamicTagsField::class,
        'objectList'      =>  Form\Fields\ObjectListField::class,
        'list'      =>  Form\Fields\ListField::class,
        'sfcLocal'  =>  Form\Fields\SfcLocalField::class,
        'sfcApi'    =>  Form\Fields\SfcApiField::class,
        'imageVerify'    =>  Form\Fields\ImageVerifyField::class,
    ];


    public function __construct(){
//        $components = config('lowcode.forms',[]);
//        $methods = array_merge($this->methods,$components);
//        foreach ($methods as $method=>$class){
//            self::macro($method,$class);
//        }
    }

    /**
     * input组label
     * @param string|null $label
     * @return $this
     */
    public function groupLabel(mixed $label): static
    {
        $this->elements[] = UI::inputGroupLabel($label);
        return $this;
    }

    /**
     * html内容
     * @param mixed|null $schema
     * @param string|null $label
     * @return $this
     */
    public function html(mixed $schema = null, string $label = null): static
    {
        if ($label||!$this->noItem){
            $formField = new FormField($label, $schema);
        }else{
            $formField = $schema;
        }
        $this->elements[] = $formField;
        return $this;
    }

    /**
     * 增加分割线
     * @param string $text
     * @return Divider
     */
    public function divider(string $text = ""):Divider
    {
        $driver = new Divider($text);
        return tap($driver,function ($driver){
            $this->elements[] = $driver;
        });
    }

    /**
     * @return string
     */
    public function getBindKey(): string
    {
        return $this->bindKey;
    }

    /**
     * @param string $bindKey
     * @return $this
     */
    public function setBindKey(string $bindKey): static
    {
        $this->bindKey = $bindKey;
        return $this;
    }

    /**
     * @return string
     */
    public function getMode(): string
    {
        return $this->mode;
    }

    /**
     * @param string $mode
     * @return $this
     */
    public function setMode(string $mode): static
    {
        $this->mode = $mode;
        return $this;
    }


    /**
     * @param $field
     * @param $rule
     * @return $this
     */
    public function rule($field, $rule): static
    {
        $fieldRule = $this->rules[$field];
        if (!$fieldRule){
            $fieldRule = [];
        }
        $fieldRule[] = $rule;
        $this->rules[$field] = $fieldRule;
        return $this;
    }

    /**
     * @param $rules
     * @return $this
     */
    public function rules($rules): static
    {
        $this->rules = $rules;
        return $this;
    }

    /**
     * @return $this
     */
    public function setNoItem(): static
    {
        $this->noItem = true;
        return $this;
    }

    /**
     * @return bool
     */
    public function getNoItem(): bool
    {
        return $this->noItem;
    }

    /**
     * @param Closure|null $closure
     * @return $this
     */
    public function element(Closure $closure = null): static
    {
        !is_null($closure)&&call_user_func($closure,$this->element);
        return $this;
    }

    /**
     * Dynamically handle calls to the class.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     *
     * @throws BadMethodCallException
     */
    public function __call($method, $parameters)
    {
        if (!static::hasMacro($method)) {
            if (method_exists($this->form,$method)) {
                return $this->form->{$method}(...$parameters);
            }else{
                throw new BadMethodCallException(sprintf(
                    'Method %s::%s does not exist.', static::class, $method
                ));
            }
        }
        $macro = static::$macros[$method];
        array_unshift($parameters,$this);
        $object = new $macro(...$parameters);
        $this->elements[] = $object;
        if ($object instanceof Closure) {
            return call_user_func_array($object->bindTo($this, static::class),$parameters);
        }
        return $object;
    }
}
